/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * gtk-glxinfo.c
 * Copyright (C) 2012 Kevin DeKorte <kdekorte@gmail.com>
 * 
 * gtk-glxinfo is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * gtk-glxinfo is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "gtk-glxinfo.h"

#include <glib/gi18n.h>

#define GLX_GLXEXT_PROTOTYPES
#define GL_GLEXT_PROTOTYPES

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <GL/gl.h>
#include <GL/glx.h>
#include <stdlib.h>

#ifndef GLX_NONE_EXT
#define GLX_NONE_EXT  0x8000
#endif

#ifndef GLX_TRANSPARENT_RGB
#define GLX_TRANSPARENT_RGB 0x8008
#endif

#ifndef GLX_RGBA_BIT
#define GLX_RGBA_BIT			0x00000001
#endif

#ifndef GLX_COLOR_INDEX_BIT
#define GLX_COLOR_INDEX_BIT		0x00000002
#endif

G_DEFINE_TYPE(Gtkglxinfo, gtk_glxinfo, GTK_TYPE_APPLICATION);

int string_cmp(const void *a, const void *b)
{
    const char **ia = (const char **) a;
    const char **ib = (const char **) b;
    return strcmp(*ia, *ib);
}


static void add_extension_tab(GtkWidget * notebook, gchar * tabName, const char *extensions)
{

    GtkAdjustment *hAdj, *vAdj;
    GtkWidget *viewport;
    GtkWidget *scroller;
    GtkWidget *grid;
    GtkWidget *label;
    gint row, col;
    gchar **split;
    GtkStyle *style;

    hAdj = gtk_adjustment_new(0.0, 0.0, 101.0, 0.1, 1.0, 1.0);
    vAdj = gtk_adjustment_new(0.0, 0.0, 101.0, 0.1, 1.0, 1.0);
    viewport = gtk_viewport_new(hAdj, vAdj);
    style = gtk_widget_get_style(notebook);
    gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
    gtk_widget_modify_bg(viewport, GTK_STATE_NORMAL, &(style->base[0]));
    scroller = gtk_scrolled_window_new(hAdj, vAdj);
    gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroller), GTK_SHADOW_NONE);
    grid = gtk_grid_new();
    gtk_grid_set_row_homogeneous(GTK_GRID(grid), FALSE);
    label = gtk_label_new(tabName);
    gtk_container_add(GTK_CONTAINER(viewport), grid);
    gtk_container_add(GTK_CONTAINER(scroller), viewport);
    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scroller, label);
    row = 0;
    col = 1;

    split = g_strsplit(extensions, " ", 0);

    qsort(split, g_strv_length(split), sizeof(char *), string_cmp);

    while (split[row] != NULL) {
        if (strlen(split[row]) > 0) {
            label = gtk_label_new(split[row]);
            gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
            gtk_grid_attach(GTK_GRID(grid), label, col, row, 1, 1);
        }
        row++;
    }

    g_strfreev(split);
}




/* Create a new window loading a file */
static void gtk_glxinfo_new_window(GApplication * app, GFile * file)
{
    GtkWidget *window;
    GtkWidget *screenNotebook;
    GtkWidget *notebook;
    GtkWidget *grid;
    GtkWidget *label;

    gchar *displayName = NULL;
    gchar *labelText = NULL;
    Display *dpy;
    gint scrnum;
    Bool allowDirect = True;
    Window win;
    int attribSingle[] = {
        GLX_RGBA,
        GLX_RED_SIZE, 1,
        GLX_GREEN_SIZE, 1,
        GLX_BLUE_SIZE, 1,
        None
    };
    int attribDouble[] = {
        GLX_RGBA,
        GLX_RED_SIZE, 1,
        GLX_GREEN_SIZE, 1,
        GLX_BLUE_SIZE, 1,
        GLX_DOUBLEBUFFER,
        None
    };
    XSetWindowAttributes attr;
    unsigned long mask;
    Window root;
    GLXContext ctx = NULL;
    XVisualInfo *visinfo;
    int width = 100, height = 100;
    gint row, col;

    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    //gtk_window_set_resizable (GTK_WINDOW(window), FALSE);
    gtk_window_resize(GTK_WINDOW(window), 480, 300);

    screenNotebook = gtk_notebook_new();
    gtk_notebook_set_tab_pos(GTK_NOTEBOOK(screenNotebook), GTK_POS_LEFT);
    gtk_container_add(GTK_CONTAINER(window), screenNotebook);

    dpy = XOpenDisplay(displayName);
    if (!dpy) {
        fprintf(stderr, "Error: unable to open display %s\n", XDisplayName(displayName));
        return;
    }
    //printf("Number of Screens = %i\n", ScreenCount(dpy));
    //printf("name of display: %s\n", DisplayString(dpy));
    labelText = g_strdup_printf(_("glxinfo - %s"), DisplayString(dpy));
    gtk_window_set_title(GTK_WINDOW(window), labelText);
    g_free(labelText);

    for (scrnum = 0; scrnum < ScreenCount(dpy); scrnum++) {

        labelText = g_strdup_printf(_("Screen %i"), scrnum);
        label = gtk_label_new(labelText);
        g_free(labelText);
        notebook = gtk_notebook_new();
        gtk_notebook_append_page(GTK_NOTEBOOK(screenNotebook), notebook, label);

        root = RootWindow(dpy, scrnum);
        visinfo = glXChooseVisual(dpy, scrnum, attribSingle);
        if (!visinfo)
            visinfo = glXChooseVisual(dpy, scrnum, attribDouble);

        if (visinfo)
            ctx = glXCreateContext(dpy, visinfo, NULL, allowDirect);

#ifdef GLX_VERSION_1_3
        if (!visinfo) {
            int fbAttribSingle[] = {
                GLX_RENDER_TYPE, GLX_RGBA_BIT,
                GLX_RED_SIZE, 1,
                GLX_GREEN_SIZE, 1,
                GLX_BLUE_SIZE, 1,
                GLX_DOUBLEBUFFER, GL_FALSE,
                None
            };
            int fbAttribDouble[] = {
                GLX_RENDER_TYPE, GLX_RGBA_BIT,
                GLX_RED_SIZE, 1,
                GLX_GREEN_SIZE, 1,
                GLX_BLUE_SIZE, 1,
                GLX_DOUBLEBUFFER, GL_TRUE,
                None
            };
            GLXFBConfig *configs = NULL;
            int nConfigs;

            configs = glXChooseFBConfig(dpy, scrnum, fbAttribSingle, &nConfigs);
            if (!configs)
                configs = glXChooseFBConfig(dpy, scrnum, fbAttribDouble, &nConfigs);

            if (configs) {
                visinfo = glXGetVisualFromFBConfig(dpy, configs[0]);
                ctx = glXCreateNewContext(dpy, configs[0], GLX_RGBA_TYPE, NULL, allowDirect);
                XFree(configs);
            }
        }
#endif

        if (!visinfo) {
            fprintf(stderr, "Error: couldn't find RGB GLX visual or fbconfig\n");
            return;
        }

        if (!ctx) {
            fprintf(stderr, "Error: glXCreateContext failed\n");
            XFree(visinfo);
            return;
        }

        attr.background_pixel = 0;
        attr.border_pixel = 0;
        attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone);
        attr.event_mask = StructureNotifyMask | ExposureMask;
        mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
        win = XCreateWindow(dpy, root, 0, 0, width, height,
                            0, visinfo->depth, InputOutput, visinfo->visual, mask, &attr);

        if (glXMakeCurrent(dpy, win, ctx)) {

            grid = gtk_grid_new();
            // gtk_grid_set_row_homogeneous(GTK_GRID(grid), TRUE);
            labelText = g_strdup_printf(_("Summary"));
            label = gtk_label_new(labelText);
            g_free(labelText);
            gtk_notebook_append_page(GTK_NOTEBOOK(notebook), grid, label);
            row = 1;
            col = 1;

            label = gtk_label_new(_("Direct Rendering:"));
            gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
            gtk_grid_attach(GTK_GRID(grid), label, col, row, 1, 1);
            label = gtk_label_new((glXIsDirect(dpy, ctx) ? _("Enabled") : _("Disabled")));
            gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
            gtk_grid_attach(GTK_GRID(grid), label, col + 1, row++, 1, 1);


            label = gtk_label_new(_("Server GLX Vendor:"));
            gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
            gtk_grid_attach(GTK_GRID(grid), label, col, row, 1, 1);
            label = gtk_label_new(glXQueryServerString(dpy, scrnum, GLX_VENDOR));
            gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
            gtk_grid_attach(GTK_GRID(grid), label, col + 1, row++, 1, 1);

            label = gtk_label_new(_("Server GLX Version:"));
            gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
            gtk_grid_attach(GTK_GRID(grid), label, col, row, 1, 1);
            label = gtk_label_new(glXQueryServerString(dpy, scrnum, GLX_VERSION));
            gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
            gtk_grid_attach(GTK_GRID(grid), label, col + 1, row++, 1, 1);

            row++;
            label = gtk_label_new(_("Client GLX Vendor:"));
            gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
            gtk_grid_attach(GTK_GRID(grid), label, col, row, 1, 1);
            label = gtk_label_new(glXGetClientString(dpy, GLX_VENDOR));
            gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
            gtk_grid_attach(GTK_GRID(grid), label, col + 1, row++, 1, 1);

            label = gtk_label_new(_("Client GLX Version:"));
            gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
            gtk_grid_attach(GTK_GRID(grid), label, col, row, 1, 1);
            label = gtk_label_new(glXGetClientString(dpy, GLX_VERSION));
            gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
            gtk_grid_attach(GTK_GRID(grid), label, col + 1, row++, 1, 1);

            row++;
            label = gtk_label_new(_("OpenGL Vendor:"));
            gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
            gtk_grid_attach(GTK_GRID(grid), label, col, row, 1, 1);
            label = gtk_label_new((const char *) glGetString(GL_VENDOR));
            gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
            gtk_grid_attach(GTK_GRID(grid), label, col + 1, row++, 1, 1);

            label = gtk_label_new(_("OpenGL Renderer:"));
            gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
            gtk_grid_attach(GTK_GRID(grid), label, col, row, 1, 1);
            label = gtk_label_new((const char *) glGetString(GL_RENDERER));
            gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
            gtk_grid_attach(GTK_GRID(grid), label, col + 1, row++, 1, 1);

            label = gtk_label_new(_("OpenGL Version:"));
            gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
            gtk_grid_attach(GTK_GRID(grid), label, col, row, 1, 1);
            label = gtk_label_new((const char *) glGetString(GL_VERSION));
            gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
            gtk_grid_attach(GTK_GRID(grid), label, col + 1, row++, 1, 1);

#ifdef GL_VERSION_2_0
            const char *glVersion = (const char *) glGetString(GL_VERSION);
            if (glVersion[0] >= '2' && glVersion[1] == '.') {
                label = gtk_label_new(_("OpenGL Shading Language Version:"));
                gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
                gtk_grid_attach(GTK_GRID(grid), label, col, row, 1, 1);
                label = gtk_label_new((char *) glGetString(GL_SHADING_LANGUAGE_VERSION));
                gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
                gtk_grid_attach(GTK_GRID(grid), label, col + 1, row++, 1, 1);
            }
#endif
            label = gtk_label_new("");
            gtk_label_set_markup(GTK_LABEL(label), _("<b>Patented Features</b>"));
            gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
            gtk_grid_attach(GTK_GRID(grid), label, col, row++, 1, 1);

            label = gtk_label_new(_("S3 Texture Compression:"));
            gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
            gtk_grid_attach(GTK_GRID(grid), label, col, row, 1, 1);
            label =
                gtk_label_new(((strstr((const char *) glGetString(GL_EXTENSIONS), "GL_S3_s3tc") !=
                                NULL) ? _("Enabled") : _("Disabled")));
            gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
            gtk_grid_attach(GTK_GRID(grid), label, col + 1, row++, 1, 1);

            label = gtk_label_new(_("Floating Point Textures:"));
            gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
            gtk_grid_attach(GTK_GRID(grid), label, col, row, 1, 1);
            label =
                gtk_label_new(((strstr((const char *) glGetString(GL_EXTENSIONS), "ARB_texture_float") !=
                                NULL) ? _("Enabled") : _("Disabled")));
            gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
            gtk_grid_attach(GTK_GRID(grid), label, col + 1, row++, 1, 1);

            labelText = g_strdup_printf(_("Server Extensions"));
            add_extension_tab(notebook, labelText, glXQueryServerString(dpy, scrnum, GLX_EXTENSIONS));
            g_free(labelText);

            labelText = g_strdup_printf(_("Client Extensions"));
            add_extension_tab(notebook, labelText, glXGetClientString(dpy, GLX_EXTENSIONS));
            g_free(labelText);

            labelText = g_strdup_printf(_("OpenGL Extensions"));
            add_extension_tab(notebook, labelText, (const char *) glGetString(GL_EXTENSIONS));
            g_free(labelText);

            //print_extension_list(glExtensions);
            //if (limits)
            //print_limits(glExtensions);
        } else {
            fprintf(stderr, "Error: glXMakeCurrent failed\n");
        }

        glXDestroyContext(dpy, ctx);
        XFree(visinfo);
        XDestroyWindow(dpy, win);

    }

    XCloseDisplay(dpy);

    gtk_window_set_application(GTK_WINDOW(window), GTK_APPLICATION(app));
    gtk_widget_show_all(GTK_WIDGET(window));
}


/* GApplication implementation */
static void gtk_glxinfo_activate(GApplication * application)
{
    gtk_glxinfo_new_window(application, NULL);
}

static void gtk_glxinfo_open(GApplication * application, GFile ** files, gint n_files, const gchar * hint)
{
    gint i;

    for (i = 0; i < n_files; i++)
        gtk_glxinfo_new_window(application, files[i]);
}

static void gtk_glxinfo_init(Gtkglxinfo * object)
{

}

static void gtk_glxinfo_finalize(GObject * object)
{

    G_OBJECT_CLASS(gtk_glxinfo_parent_class)->finalize(object);
}

static void gtk_glxinfo_class_init(GtkglxinfoClass * klass)
{
    G_APPLICATION_CLASS(klass)->activate = gtk_glxinfo_activate;
    G_APPLICATION_CLASS(klass)->open = gtk_glxinfo_open;

    G_OBJECT_CLASS(klass)->finalize = gtk_glxinfo_finalize;
}

Gtkglxinfo *gtk_glxinfo_new(void)
{
    g_type_init();

    return g_object_new(gtk_glxinfo_get_type(),
                        "application-id", "org.gnome.gtk_glxinfo", "flags", G_APPLICATION_HANDLES_OPEN, NULL);
}
